import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;

public class Protocall extends Thread {
	public enum Method {
		IDLE, ERROR, GET, BREW, PROPFIND, WHEN, END
	}

	String OK = "HTCPCP/1.0 200 OK\r\n\r\n";
	String OK_PROPFIND = "HTCPCP/1.0 200 OK\r\n";
	String FINISHED_BREWING = "HTCPCP/1.0 113 Finished Brewing\r\n\r\n";
	String NOT_FOUND = "HTCPCP/1.0 404 Not Found\r\n\r\n";
	String NOT_ACCEPTABLE = "HTCPCP/1.0 406 Not Acceptable\r\n\r\n";
	String POT_BUSY = "HTCPCP/1.0 510 Pot busy\r\n\r\n";
	String STILL_BREWING = "HTCPCP/1.0 105 Still Brewing\r\n\r\n";
	String STILL_POURING = "HTCPCP/1.0 122 Still Pouring\r\n\r\n";
	String OVERFLOWED = "HTCPCP/1.0 515 Pot Overflowed\r\n\r\n";
	String POT_COLD = "HTCPCP/1.0 419 Pot Cold\r\n\r\n";
	String FINISHED_POURING = "HTCPCP/1.0 115 Finished Pouring\r\n\r\n";
	String NOT_POURING = "HTCPCP/1.0 421 Not Pouring\r\n\r\n";

	StreamSocket client;
	Receiver clientReceiver;
	ArrayList<CoffeePot> pots;
	String request;
	Method method;
	Parser parser;
	Order order;
	int index;
	boolean clientsPot[];
	boolean debug;

	public Protocall(Socket acceptedSocket, ArrayList<CoffeePot> pots, boolean debug) {
		try {
			client = new StreamSocket(acceptedSocket);
			clientReceiver = new Receiver(client);
			this.pots = pots;
			parser = new Parser();
			method = Method.IDLE;
			clientsPot = new boolean[this.pots.size()];
			this.debug = debug;
			for (int i = 0; i < this.pots.size(); i++) {
				clientsPot[i] = false;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public void run() {
		clientReceiver.start();// Start Receiver
		while (client.isOpen()) {
			switch (method) {
			default: {
				try {
					Thread.sleep(500L);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				if ((request = clientReceiver.getInput()) != "") {
					// Check for client input
					if (debug == true) {// DEBUG MESSAGE
						System.out.print("Received from: "
								+ client.displayDetail() + "\n" + request);
					}
					method = parser.parseHeader(request, pots.size());// Get
					index = parser.getPotNo();// Get pot number // method
					if (method == Method.ERROR) {// If input invalid
						break;// Send client error
					}
				}
				clientReceiver.clear();
				// Notify client that pot has brewed
				for (int i = 0; i < pots.size(); i++) {
					if ((pots.get(i).getPotState() == CoffeePot.State.NOT_BREWING)
							&& (clientsPot[i] == true)) {
						// Send 113 Finished Brewing
						client.sendString(FINISHED_BREWING);
						if (debug == true) {// DEBUG MESSAGE
							System.out.print("Sent to: "
									+ client.displayDetail() + "\n"
									+ FINISHED_BREWING);
						}
						pots.get(i).setPotState(CoffeePot.State.POURING);
					}
				}
				break;
			}
			case ERROR: {

				if (parser.getErrorNo() == 404) {
					// Send 404 Not Found
					client.sendString(NOT_FOUND);
					if (debug == true) {// DEBUG MESSAGE
						System.out.print("Sent to: " + client.displayDetail()
								+ "\n" + NOT_FOUND);
					}
				} else if (parser.getErrorNo() == 406) {
					// Send 406 Not acceptable
					client.sendString(NOT_ACCEPTABLE);
					if (debug == true) {// DEBUG MESSAGE
						System.out.print("Sent to: " + client.displayDetail()
								+ "\n" + NOT_ACCEPTABLE);
					}
				}
				clientReceiver.clear();
				method = Method.IDLE;
				break;
			}
			case BREW: {
				if (pots.get(index).getPotState() == CoffeePot.State.IDLE) {
					parser.addToOrder(request);
					order = parser.getOrder();
					order = pots.get(index).takeOrder(order);
					if ((order.hasMilk == order.wantsMilk)
							&& (order.hasSyrup == order.wantsSyrup)
							&& (order.hasAlcohol == order.wantsAlcohol)) {
						pots.get(index).setPotState(CoffeePot.State.BREWING);
						clientsPot[index] = true;
					} else {
						pots.get(index).cancelOrder();
						method = Method.PROPFIND;
						break;
					}
				} else {
					// Send 510 Pot Busy
					client.sendString(POT_BUSY);
					if (debug == true) {// DEBUG MESSAGE
						System.out.print("Sent to: " + client.displayDetail()
								+ "\n" + POT_BUSY);
					}
				}
				clientReceiver.clear();
				method = Method.IDLE;
				break;
			}
			case GET: {
				if (pots.get(index).getPotState() == CoffeePot.State.READY) {
					// Send 200 OK
					clientsPot[index] = false;
					client.sendString(OK);
					if (debug == true) {// DEBUG MESSAGE
						System.out.print("Sent to: " + client.displayDetail()
								+ "\n" + OK);
					}
					pots.get(index).setPotState(CoffeePot.State.IDLE);
					method = Method.END;
					break;
				} else if (pots.get(index).getPotState() == CoffeePot.State.BREWING) {
					// Send 105 Still Brewing
					client.sendString(STILL_BREWING);
					if (debug == true) {// DEBUG MESSAGE
						System.out.print("Sent to: " + client.displayDetail()
								+ "\n" + STILL_BREWING);
					}
				} else if (pots.get(index).getPotState() == CoffeePot.State.POURING) {
					// Send 122 Still Pouring
					client.sendString(STILL_POURING);
					if (debug == true) {// DEBUG MESSAGE
						System.out.print("Sent to: " + client.displayDetail()
								+ "\n" + STILL_POURING);
					}
				} else if (pots.get(index).getPotState() == CoffeePot.State.OVERFLOWED) {
					// Send 515 Pot Overflowed
					client.sendString(OVERFLOWED);
					if (debug == true) {// DEBUG MESSAGE
						System.out.print("Sent to: " + client.displayDetail()
								+ "\n" + OVERFLOWED);
					}
				} else if (pots.get(index).getPotState() == CoffeePot.State.IDLE) {
					// Send 200 OK
					client.sendString(OK);
					if (debug == true) {// DEBUG MESSAGE
						System.out.print("Sent to: " + client.displayDetail()
								+ "\n" + OK);
					}
				} else if (pots.get(index).getPotState() == CoffeePot.State.COLD) {
					// Send 419 Pot Cold
					if (clientsPot[index] == true) {// If this is clients pot
													// then
													// reset pot
						clientsPot[index] = false;
						client.sendString(POT_COLD);
						if (debug == true) {// DEBUG MESSAGE
							System.out.print("Sent to: "
									+ client.displayDetail() + "\n" + POT_COLD);
						}
						pots.get(index).setPotState(CoffeePot.State.IDLE);
						method = Method.END;
						break;
					} else {// Else just display status
						client.sendString(POT_COLD);
						if (debug == true) {// DEBUG MESSAGE
							System.out.print("Sent to: "
									+ client.displayDetail() + "\n" + POT_COLD);
						}
					}
				} else {
					// Send 200 OK
					client.sendString(OK);
					if (debug == true) {// DEBUG MESSAGE
						System.out.print("Sent to: " + client.displayDetail()
								+ "\n" + OK);
					}
				}
				clientReceiver.clear();
				method = Method.IDLE;
				break;
			}
			case PROPFIND: {
				// Send 200 OK
				String message = OK_PROPFIND + "accept-additions:"
						+ pots.get(index).additionsToString() + "\r\n\r\n";
				client.sendString(message);
				if (debug == true) {// DEBUG MESSAGE
					System.out.print("Sent to: " + client.displayDetail()
							+ "\n" + message);
				}
				clientReceiver.clear();
				method = Method.IDLE;
				break;
			}
			case WHEN: {
				if (pots.get(index).getPotState() == CoffeePot.State.POURING) {
					// Send 115 Finished Pouring
					client.sendString(FINISHED_POURING);
					if (debug == true) {// DEBUG MESSAGE
						System.out.print("Sent to: " + client.displayDetail()
								+ "\n" + FINISHED_POURING);
					}
					pots.get(index).setPotState(CoffeePot.State.READY);

				} else if (pots.get(index).getPotState() == CoffeePot.State.OVERFLOWED) {
					// Send 515 Pot Overflowed
					if (clientsPot[index] == true) {// If this is clients pot
													// then reset pot
						clientsPot[index] = false;
						client.sendString(OVERFLOWED);
						if (debug == true) {// DEBUG MESSAGE
							System.out.print("Sent to: "
									+ client.displayDetail() + "\n"
									+ OVERFLOWED);
						}
						pots.get(index).setPotState(CoffeePot.State.IDLE);
						method = Method.END;
						break;
					} else {// Else just display status
						client.sendString(OVERFLOWED);
						if (debug == true) {// DEBUG MESSAGE
							System.out.print("Sent to: "
									+ client.displayDetail() + "\n"
									+ OVERFLOWED);
						}
					}
				} else {
					// Send 421 Not Pouring
					client.sendString(NOT_POURING);
					if (debug == true) {// DEBUG MESSAGE
						System.out.print("Sent to: " + client.displayDetail()
								+ "\n" + NOT_POURING);
					}
				}
				clientReceiver.clear();
				method = Method.IDLE;
				break;
			}
			case END: {

				try {
					client.kill();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			}
		}
	}
}
